package com.youlu.campus.service.order.impl;

import com.alibaba.fastjson.JSON;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.base.order.vo.OrderRequest;
import com.youlu.campus.common.utils.ExcelUtil;
import com.youlu.campus.common.utils.TimeUtils;
import com.youlu.campus.entity.*;
import com.youlu.campus.entity.VO.req.SingleOrderBatchQueryVO;
import com.youlu.campus.entity.data.SingleOrderReverseImport;
import com.youlu.campus.entity.domain.OrderImport;
import com.youlu.campus.entity.domain.SingleCombineDomain;
import com.youlu.campus.entity.domain.SingleMatchDomain;
import com.youlu.campus.entity.domain.SingleOrderImport;
import com.youlu.campus.entity.order.SingleOrderBatch;
import com.youlu.campus.entity.order.SingleOrderBatchDetail;
import com.youlu.campus.service.activity.ActivityService;
import com.youlu.campus.service.base.LockService;
import com.youlu.campus.service.ca.CaInfoService;
import com.youlu.campus.service.ca.CaMaterialConfigService;
import com.youlu.campus.service.ca.SingleOrderConfigService;
import com.youlu.campus.service.order.OrderService;
import com.youlu.campus.service.order.SingleOrderBatchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class SingleOrderBatchServiceImpl implements SingleOrderBatchService {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private ActivityService activityService;
    @Autowired
    private LockService lockService;
    @Lazy
    @Autowired
    private CaInfoService caInfoService;
    @Autowired
    private CaMaterialConfigService caMaterialConfigService;
    @Autowired
    private SingleOrderConfigService singleOrderConfigService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private OrderService orderService;
    // 合单预操作
    private String SINGLE_ORDER_PRE_KEY = "com.luyou.single-order.pre:%s";
    // 合单取消操作
    private String SINGLE_ORDER_CANCEL_KEY = "com.luyou.single-order.cancel:%s";
    private static Long orderNoExpireTime = 365 * 24 * 3600 * 1000L;
    final private static String orderNoPreKey = "com.luyou.campus.single-order.no";

    public static ConcurrentHashMap<String, CaInfo> caInfoCache = new ConcurrentHashMap<>();
    public static ConcurrentHashMap<String, CaMaterialConfig> caMaterialCache = new ConcurrentHashMap<>();

    @Override
    public Page<SingleOrderBatch> list(SingleOrderBatchQueryVO req) {
        log.info(":>>> 开始获取合单批次:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getActivityId())) {
            log.error(":>>> 参数错误");
            throw new BusinessException("参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("activityId").is(req.getActivityId()));
        if (StringUtils.isNotBlank(req.getStatus())) {
            query.addCriteria(Criteria.where("status").is(req.getStatus()));
        }
        if (StringUtils.isNotBlank(req.getOptLoginName())) {
            query.addCriteria(Criteria.where("optLoginName").is(req.getOptLoginName()));
        }
        query.addCriteria(Criteria.where("deleted").ne(true));
        long count = mongoTemplate.count(query, SingleOrderBatch.class);
        PageRequest pageRequest = PageRequest.of(req.getPage() - 1, req.getPageSize());
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort=Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<SingleOrderBatch> infos = mongoTemplate.find(query.with(pageRequest), SingleOrderBatch.class);
        return PageableExecutionUtils.getPage(infos, pageRequest, () -> count);
    }

    @Override
    public boolean update(SingleOrderBatch req) {
        log.info(":>>> 开始修改合单批次:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getId())) {
            log.error(":>>> 参数错误");
            throw new BusinessException("参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        Update update = new Update();
        if (StringUtils.isNotBlank(req.getStatus())) {
            update.set("status", req.getStatus());
        }
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, SingleOrderBatch.class);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public boolean create(SingleOrderBatch req, byte[] inputStream, String fileName, String optLoginName) {
        if (Objects.isNull(req) || Objects.isNull(inputStream) || StringUtils.isBlank(fileName) || StringUtils.isBlank(optLoginName)) {
            log.error(":>>> 创建发货批次参数错误");
            throw new BusinessException("创建发货批次参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("activityId").is(req.getActivityId()).and("fileName").is(fileName).and("status").is("0"));
        boolean e = mongoTemplate.exists(query, SingleOrderBatch.class);
        if (e) {
            log.error(":>>> 发货批次已经存在:{},{}", req.getActivityId(), fileName);
            throw new BusinessException("发货批次已经存在");
        }
        ActivityInfo activityInfo = activityService.findOne(req.getActivityId());
        req.setCreatedTime(new Date());
        req.setOptLoginName(optLoginName);
        req.setFileName(fileName);
        req.setId(null);
        req.setActivityName(activityInfo.getId());
        req = mongoTemplate.insert(req);
        this.importSingleBatch(inputStream, req.getId());
        req.setStatus("2");
        this.update(req);
        return true;
    }

    @Override
    public SingleOrderBatch findOne(String id) {
        log.info(":>>> 开始查找合单批次:{}", JSON.toJSONString(id));
        if (StringUtils.isBlank(id)) {
            log.error(":>>> 参数错误");
            throw new BusinessException("参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        SingleOrderBatch batch = mongoTemplate.findOne(query, SingleOrderBatch.class);
        Query queryDetail = new Query().addCriteria(Criteria.where("singleOrderBatchId").is(id));
        List<SingleOrderBatchDetail> details = mongoTemplate.find(queryDetail, SingleOrderBatchDetail.class);
        batch.setDetails(details);
        return mongoTemplate.findOne(query, SingleOrderBatch.class);
    }

    @Override
    public boolean cancel(String id) {
        // 取消操作更新批次状态,删除订单中的合单参数
        SingleOrderBatch batch = this.findOne(id);
        if (Objects.isNull(batch)) {
            log.error(":.>> 合单批次不存在:{}", id);
            throw new BusinessException("合单批次不存在");
        }
        //状态,0:待处理,1:取消,2:预合单,3:合单中,4:待确认,5:已确认
        if ("1".equals(batch.getStatus())) {
            log.error(":>>> 合单取消状态错误:{}", batch.getStatus());
            throw new BusinessException("合单取消状态错误");
        }
        String key = String.format(SINGLE_ORDER_CANCEL_KEY, id);
        boolean lock = lockService.tryLock(key, 1, TimeUnit.DAYS);
        if (!lock) {
            log.error(":>>> 合单取消状态错误:{},有合单取消中批次", id);
            throw new BusinessException("有预合单取消状态错误,有合单取消中批次");
        }
        this.cancelOrder(id);
        Query query = checkIdAndQuery(id);
        Update update = new Update();
        //状态,0:待处理,1:取消,2:预合单,3:合单中,4:待确认,5:已确认
        update.set("status", "1");
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, SingleOrderBatch.class);
        lockService.unLock(key);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public boolean preDo(String id) {
        SingleOrderBatch batch = this.findOne(id);
        if (Objects.isNull(batch)) {
            log.error(":.>> 合单批次不存在:{}", id);
            throw new BusinessException("合单批次不存在");
        }
        if (!"2".equals(batch.getStatus())) {
            log.error(":>>> 预合单状态错误:{}", batch.getStatus());
            throw new BusinessException("预合单状态错误");
        }
        String key = String.format(SINGLE_ORDER_PRE_KEY, id);
        boolean lock = lockService.tryLock(key, 1, TimeUnit.DAYS);
        if (!lock) {
            log.error(":>>> 预合单状态错误:{},有预操作中订单", id);
            throw new BusinessException("有预操作中订单");
        }
        this.preDoOrder(id, batch);
        Query query = checkIdAndQuery(id);
        Update update = new Update();
        //状态,0:待处理,1:取消,2:预合单,3:合单中,4:待确认,5:已确认
        update.set("status", "3");
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, SingleOrderBatch.class);
        lockService.unLock(key);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public boolean done(String id, String ip, String optUserName) {
        log.info(":>>> 合单批次确认:{},IP:{},操作人:{}", id, ip, optUserName);
        SingleOrderBatch batch = this.findOne(id);
        if (Objects.isNull(batch)) {
            log.error(":.>> 合单批次不存在:{}", id);
            throw new BusinessException("合单批次不存在");
        }
        if (!"3".equals(batch.getStatus())) {
            log.error(":>>> 合单确认状态错误:{}", batch.getStatus());
            throw new BusinessException("合单确认状态错误");
        }
        // 处理合单操作
        boolean re = this.doneOrder(id, ip, optUserName);
        log.info(":>>> 合单确认:{},操作结果:{}", id, re);
        if (re) {
            Query query = checkIdAndQuery(id);
            Update update = new Update();
            //状态,0:待处理,1:取消,2:预合单,3:合单中,4:待确认,5:已确认
            update.set("status", "5");
            update.set("updatedTime", new Date());
            UpdateResult updateResult = mongoTemplate.updateFirst(query, update, SingleOrderBatch.class);
            return updateResult.getModifiedCount() > 0 ? true : false;
        }
        return false;

    }


    @Override
    public boolean delete(String id) {
        Query query = checkIdAndQuery(id);
        Update update = new Update();
        //状态,0:待处理,1:取消,2:预合单,3:合单中,4:待确认,5:已确认
        update.set("deleted", true);
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, SingleOrderBatch.class);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public boolean reverse(byte[] inputStream, String fileName, String optLoginName, String activityId) {
        List<SingleOrderReverseImport> models = ExcelUtil.readData(new ByteArrayInputStream(inputStream), SingleOrderReverseImport.class);
        if (CollectionUtils.isEmpty(models)) {
            return false;
        }
        Date date = new Date();
        log.info(":>>> 手动合单的订单大小:{}", models.size());
        int counter = 1;
        for (SingleOrderReverseImport s : models) {
            log.info(":>>> 开始处理第:{} 行", counter);
            if (StringUtils.isBlank(s.getMaterialIds())) {
                log.error(":>>> 第:{} 行,材质配置为空", counter);
                continue;
            }
            List<String> ms = new ArrayList<>();
            List<String> subIds = new ArrayList<>();
            String array[] = null;
            if (s.getMaterialIds().contains(",")) {
                array = s.getMaterialIds().split(",");
            } else {
                array = new String[1];
                array[0] = s.getMaterialIds();
            }
            for (int i = 0; i < array.length; i++) {
                ms.add(array[i]);
            }
            Query query = new Query().addCriteria(Criteria.where("mobile").is(s.getMobile()).and("activityId").is(activityId).and("status").ne("0"));
//            query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "payTime")));
            Sort sort=Sort.by(Sort.Direction.ASC, "payTime");
            query.with(sort);
            List<OrderInfo> orderInfos = mongoTemplate.find(query, OrderInfo.class);
            if (CollectionUtils.isEmpty(orderInfos)) {
                log.error("用户:{},活动:{} 订单不存在", s.getMobile(), activityId);
                continue;
            }
            List<String> orderNos = new ArrayList<>();
            for (OrderInfo orderInfo : orderInfos) {
                orderNos.add(orderInfo.getOrderNo());
            }
            Query querySub = new Query().addCriteria(Criteria.where("orderNo").in(orderNos));
//            querySub.with(new Sort(new Sort.Order(Sort.Direction.ASC, "createdTime")));
            Sort sortSub=Sort.by(Sort.Direction.ASC, "createdTime");
            querySub.with(sortSub);
            List<OOrderInfo> subs = mongoTemplate.find(querySub, OOrderInfo.class);
            if (CollectionUtils.isEmpty(subs)) {
                log.error(":>>> 订单:{} 子订单集合不存在", JSON.toJSONString(orderNos));
                continue;
            }
            for (OOrderInfo sub : subs) {
                if (ms.isEmpty()) {
                    log.error(":>>> 合单已经匹配完成,跳出循环");
                    break;
                }
                CaInfo caInfo = getCaInfo(sub.getItemInfoId());
                if (Objects.isNull(caInfo) || StringUtils.isBlank(caInfo.getMaterialConfigId())) {
                    continue;
                }
                if (ms.contains(caInfo.getMaterialConfigId())) {
                    ms.remove(caInfo.getMaterialConfigId());
                    subIds.add(sub.getOOrderNo());
                }
            }
            log.info(":>>> 子订单:{},合单的子订单:{}", s.getSubOderNo(), JSON.toJSON(subIds));
            String singleNo = this.generateCreNo();
            Query query1 = new Query().addCriteria(Criteria.where("oOrderNo").in(subIds.add(s.getSubOderNo())));
            Update update = new Update();
            update.set("singleNo", singleNo);
            update.set("updatedTime", date);
            UpdateResult updateResult = mongoTemplate.updateMulti(query1, update, OOrderInfo.class);
            log.info(":>>> 更新子订单:{},合单NO:{},结果为:{}", JSON.toJSONString(subIds.add(s.getSubOderNo())), singleNo, updateResult.getModifiedCount());
            //  更新物流
            OOrderInfo master = null;
            for (OOrderInfo sub : subs) {
                if (sub.getOOrderNo().equals(s.getSubOderNo())) {
                    log.info(":>>> 子订单:{} 是合单主订单", s.getSubOderNo());
                    master = sub;
                    break;
                }
            }
            if (Objects.nonNull(master)) {
                if (StringUtils.isNotBlank(master.getLogisticsNo())) {
                    subIds.remove(master.getOOrderNo());
                    if (!CollectionUtils.isEmpty(subIds)) {
                        Query query2 = new Query().addCriteria(Criteria.where("oOrderNo").in(subIds));
                        Update updat2 = new Update();
                        updat2.set("logisticsNo", master.getLogisticsNo());
                        updat2.set("logisticsCompany", master.getLogisticsCompany());
                        updat2.set("status", "4");
                        updat2.set("updatedTime", date);
                        UpdateResult updateResult2 = mongoTemplate.updateMulti(query2, updat2, OOrderInfo.class);
                        log.info(":>>> 更新子订单:{},物流信息:{},{},结果为:{}", JSON.toJSONString(subIds), master.getLogisticsNo(), master.getLogisticsCompany(), updateResult2.getModifiedCount());
                        //  更新物流
                    }
                }
            }

            counter++;
        }
        return true;
    }

    private Query checkIdAndQuery(String id) {
        if (StringUtils.isBlank(id)) {
            log.error(":合单批次ID 为空");
            throw new BusinessException("合单批次ID 为空");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        return query;

    }

    private void importSingleBatch(byte[] inputStream, String batchId) {
        List<OrderImport> models = ExcelUtil.readData(new ByteArrayInputStream(inputStream), OrderImport.class);
        if (CollectionUtils.isEmpty(models)) {
            return;
        }
        List<String> orderNos = new ArrayList<>();
        for (OrderImport singleOrderImport : models) {
            if (StringUtils.isNotBlank(singleOrderImport.getOrderNo()) && !orderNos.contains(singleOrderImport.getOrderNo())) {
                orderNos.add(singleOrderImport.getOrderNo());
            }
        }
        Query query = new Query().addCriteria(Criteria.where("orderNo").in(orderNos));
        Update update = new Update();
        update.set("singleOrderBatchId", batchId);
        update.set("singleOrderStatus", "1");
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateMulti(query, update, OrderInfo.class);
        log.info(":>>> 更新订单合单状态数量:{},{}", orderNos.size(), updateResult.getModifiedCount());
        UpdateResult updateResultSub = mongoTemplate.updateMulti(query, update, OOrderInfo.class);
        log.info(":>>> 更新子订单合单状态数量:{},{}", orderNos.size(), updateResultSub.getModifiedCount());
    }

    private void preDoOrder(String batchId, SingleOrderBatch batch) {
        Query query = new Query().addCriteria(Criteria.where("singleOrderBatchId").is(batchId));
        List<OrderInfo> orderInfos = mongoTemplate.find(query, OrderInfo.class);
        Date date = new Date();
        if (CollectionUtils.isEmpty(orderInfos)) {
            log.info(":>>> 批次:{} 订单列表为空", batchId);
            return;
        }
        List<String> orderNos = new ArrayList<>();
        for (OrderInfo orderInfo : orderInfos) {
            if (StringUtils.isNotBlank(orderInfo.getOrderNo()) && !orderNos.contains(orderInfo.getOrderNo())) {
                orderNos.add(orderInfo.getOrderNo());
            }
        }
        Query querySubOrder = new Query().addCriteria(Criteria.where("orderNo").in(orderNos));
        List<OOrderInfo> subs = mongoTemplate.find(querySubOrder, OOrderInfo.class);
        if (CollectionUtils.isEmpty(subs)) {
            log.info(":>>> 批次:{} 子订单列表为空", batchId);
            return;
        }
        for (OOrderInfo sub : subs) {
            Query query1 = new Query().addCriteria(Criteria.where("caInfoId").is(sub.getItemInfoId()).and("singleOrderBatchId").is(batchId));
            SingleOrderBatchDetail detail = mongoTemplate.findOne(query1, SingleOrderBatchDetail.class);
            if (Objects.isNull(detail)) {
                detail = new SingleOrderBatchDetail();
                detail.setActivityId(batch.getActivityId());
                detail.setActivityName(batch.getActivityName());
                detail.setSingleOrderBatchId(batchId);
                detail.setSingleOrderBatchName(batch.getName());
                detail.setCaInfoId(sub.getItemInfoId());
                detail.setCaInfoName(sub.getItemTitle());
                detail.setCaInfoImage(sub.getImageUrl());
                detail.setReqStock(1);
                detail.setCreatedTime(date);
                mongoTemplate.insert(detail);
            } else {
                Update update = new Update();
                update.inc("reqStock", 1);
                update.set("updatedTime", date);
                mongoTemplate.updateFirst(query1, update, SingleOrderBatchDetail.class);
            }
        }
    }

    private void cancelOrder(String batchId) {
        Date date = new Date();
        Query query = new Query().addCriteria(Criteria.where("singleOrderBatchId").is(batchId));
        Update update = new Update();
        update.set("singleOrderBatchId", "");
        update.set("singleOrderStatus", "");
        update.set("singleNo", "");
        update.set("updatedTime", date);
        UpdateResult updateResult = mongoTemplate.updateMulti(query, update, OrderInfo.class);
        log.info(":>>> 取消订单合单状态数量:{},{}", batchId, updateResult.getModifiedCount());
        UpdateResult updateResultSub = mongoTemplate.updateMulti(query, update, OOrderInfo.class);
        log.info(":>>> 取消订单合单状态子数量:{},{}", batchId, updateResultSub.getModifiedCount());
    }

    /**
     * 处理主订单合单
     *
     * @param batchId
     */
    private boolean doneOrder(String batchId, String ip, String optUserName) {
        log.info("::::>>> 合单批次确认:{},IP:{},操作人:{}", batchId, ip, optUserName);
        SingleOrderBatch batch = this.findOne(batchId);
        Query queryRule = new Query().addCriteria(Criteria.where("activityId").is(batch.getActivityId()).and("deleted").ne(true));
//        queryRule.with(new Sort(new Sort.Order(Sort.Direction.DESC, "priority")));
        Sort sort=Sort.by(Sort.Direction.DESC, "priority");
        queryRule.with(sort);
        List<SingleOrderConfig> rules = mongoTemplate.find(queryRule, SingleOrderConfig.class);
        if (CollectionUtils.isEmpty(rules)) {
            log.error(":>>> 活动没有配置合单规则:{}", batch.getActivityId());
            return false;
        }
        Query query = new Query().addCriteria(Criteria.where("singleOrderBatchId").is(batchId));
//        query.with(new Sort(new Sort.Order(Sort.Direction.DESC, "mobile")));
        Sort sortM=Sort.by(Sort.Direction.DESC, "mobile");
        query.with(sortM);
        Long count = mongoTemplate.count(query, OrderInfo.class);
        Integer pageSize = 20;
        Integer pages = count.intValue() / pageSize + 1;
        for (int page = 0; page < pages; page++) {
            log.info(":>>> 合单开始处理第:{} 页", page);
            query.limit(pageSize);
            query.skip(page * pageSize);
            List<OrderInfo> orderInfos = mongoTemplate.find(query, OrderInfo.class);
            if (CollectionUtils.isEmpty(orderInfos)) {
                log.error(":>>> 合单批次:{} 订单不存在");
                return false;
            }
            log.info(":>>> 合单批次:{} 需要合单订单数量:{}", batchId, orderInfos.size());
            CompletableFuture<String> threads[] = new CompletableFuture[orderInfos.size()];
            int j = 0;
            for (OrderInfo orderInfo : orderInfos) {
                String mobile = orderInfo.getMobile();
                String orderNo = orderInfo.getOrderNo();
                List<String> todo = new ArrayList<>();
                todo.add(orderNo);
                log.info(":>>> 开始处理订单:{},手机号:{}", orderNo, mobile);
                Query queryOrder = new Query().addCriteria(Criteria.where("singleOrderBatchId").is(batchId).and("mobile").is(mobile).and("orderNo").ne(orderInfo.getOrderNo()));
                List<OrderInfo> otherOrder = mongoTemplate.find(queryOrder, OrderInfo.class);
                for (OrderInfo orderInfo1 : otherOrder) {
                    todo.add(orderInfo1.getOrderNo());
                }
                log.info(":>>> 待合单订单编号:{},大小:{}", JSON.toJSONString(todo), todo.size());
                CompletableFuture<String> iThread = this.singleOrderThread(todo, batch.getActivityId(), batchId, rules);
                threads[j] = iThread;
                j++;
            }
            CompletableFuture.allOf(threads).join();
        }
        OrderRequest request = new OrderRequest();
        request.setIpAddress(ip);
        request.setOptUserName(optUserName);
        request.setBatchId(batchId);
        request.setActivityId(batch.getActivityId());
        orderService.exportSingleOrder(request);
        return true;
    }

    /**
     * 合单线程
     *
     * @param todo
     * @param activityId
     * @param batchId
     * @param rules
     * @return
     */
    CompletableFuture<String> singleOrderThread(List<String> todo, String activityId, String batchId, List<SingleOrderConfig> rules) {
        this.doneSubOrder(todo, activityId, batchId, rules);
        return CompletableFuture.completedFuture("OK");
    }

    /**
     * 处理子订单合单
     *
     * @param orderNos
     * @param batchId
     */
    private void doneSubOrder(List<String> orderNos, String activityId, String batchId, List<SingleOrderConfig> rules) {
        log.info(":>>> 开始处理批次:{},订单大小:{} ,订单编号:{},规则大小:{}", batchId, orderNos.size(), JSON.toJSON(orderNos), rules == null ? 0 : rules.size());
        List<List<String>> doneResult = new ArrayList<>();
        Query query = new Query().addCriteria(Criteria.where("orderNo").in(orderNos).and("singleOrderBatchId").is(batchId));
        List<OOrderInfo> subs = mongoTemplate.find(query, OOrderInfo.class);
        if (CollectionUtils.isEmpty(subs)) {
            log.error(":>>> 告警告警---:::::<<<< 处理批次:{},订单编号:{} 子订单列表为空", batchId, JSON.toJSON(orderNos));
            return;
        }
        log.info(":>>> 订单:{} 对应子订单大小:{}", JSON.toJSONString(orderNos), subs.size());
        // 商品子订单对应关系,一个商品可能对应多个订单
        HashMap<String, List<OOrderInfo>> itemMapNow = new HashMap<>();
        // 商品材质对应关系
        HashMap<String, CaMaterialConfig> itemMaterailMap = new HashMap<>();
        // 材质和商品对应关系,一个材质可能对应多个商品
        HashMap<String, List<String>> matetailItemMap = new HashMap<>();
        // 待匹配材质ID集合,使用材质ID和商品ID组合，使用,隔开
        List<String> materialIds = new ArrayList<>();
        log.info(":>>> 开始扫描子订单:{}", subs.size());
        for (OOrderInfo sub : subs) {
            if (StringUtils.isBlank(sub.getItemInfoId())) {
                log.error(":>>> 合单告警子订单编号:{},没有配置商品", sub.getOOrderNo());
                continue;
            }
            // 商品对应关系
            if (itemMapNow.containsKey(sub.getItemInfoId())) {
                itemMapNow.get(sub.getItemInfoId()).add(sub);
            } else {
                List<OOrderInfo> oOrderInfos = new ArrayList<>();
                oOrderInfos.add(sub);
                itemMapNow.put(sub.getItemInfoId(), oOrderInfos);
            }
            CaInfo caInfo = getCaInfo(sub.getItemInfoId());
            log.info(":>>> 证书:{} ::{} 配置的材质ID:{}", caInfo == null ? null : caInfo.getId(), caInfo == null ? null : caInfo.getName(), caInfo == null ? null : caInfo.getMaterialConfigId());
            if (Objects.isNull(caInfo) || StringUtils.isBlank(caInfo.getMaterialConfigId())) {
                log.error(":>>> 合单告警子订单编号:{},配置商品为空,或商品没有配置材质", sub.getOOrderNo());
            }
            log.info(":>>> 证书:{} :::{} 材质:{}", caInfo.getId(), caInfo.getName(), caInfo.getMaterialConfigId());
            if (!materialIds.contains(caInfo.getId())) {
                materialIds.add(caInfo.getId());
            }
            CaMaterialConfig config = getCaMaterialConfig(caInfo.getMaterialConfigId());
            if (Objects.nonNull(config)) {
                itemMaterailMap.put(sub.getItemInfoId(), config);
                if (matetailItemMap.containsKey(config.getId())) {
                    matetailItemMap.get(config.getId()).add(caInfo.getId());
                } else {
                    List<String> s = new ArrayList<>();
                    s.add(caInfo.getId());
                    matetailItemMap.put(config.getId(), s);
                }
            }
        }
        // 开始匹配
        log.info(":>>> ::::: 待匹配的商品材质大小:{}", materialIds == null ? 0 : materialIds.size());
        List<SingleCombineDomain> combineDomains = new ArrayList<>();
        for (SingleOrderConfig rule : rules) {
            log.info(":>>> 开始匹配:{}", rule == null ? "空" : rule.getValue());
            List<SingleOrderConfigDetail> details = singleOrderConfigService.getDetail(rule.getId());
            if (CollectionUtils.isEmpty(details)) {
                log.error(":>>> 合单规则:{} 明细配置错误", rule.getId());
                continue;
            }
            List<String> strings = new ArrayList<>();
            for (SingleOrderConfigDetail detail : details) {
                if (CollectionUtils.isEmpty(materialIds)) {
                    log.error(":>>> 证书材质:已经匹配完成,跳出外层循环");
                    break;
                }
                if (StringUtils.isNotBlank(detail.getCaMaterialConfigId()) && !strings.contains(detail.getCaMaterialConfigId())) {
                    strings.add(detail.getCaMaterialConfigId());
                }
            }
            Collections.sort(strings);
            // 获取排列组合，DD
            List<List<String>> combine = getCombine(strings);
            for (int i = 0; i < combine.size(); i++) {
                SingleCombineDomain s = new SingleCombineDomain();
                List<CaInfo> caInfos = caInfoService.findByMaterialConfigId(activityId, combine.get(i));
                log.info(":>>> 活动:{},材质列表:{} 对应的商品数量:{}", activityId, combine.get(i), caInfos == null ? 0 : caInfos.size());
                List<String> sss = new ArrayList<>();
                for (CaInfo caInfo : caInfos) {
                    sss.add(caInfo.getId());
                }
                s.setArray(sss);
                s.setLen(sss.size());
                combineDomains.add(s);
            }
        }
        Collections.sort(combineDomains);
        log.info(":>>> :::::订单商品数量:{} 明细:{}", materialIds.size(), JSON.toJSONString(materialIds));
        List<SingleMatchDomain> matchDomains = new ArrayList<>();
        for (SingleCombineDomain domain : combineDomains) {
            log.info(":>>> !!!!! 材质长度:{} 注意-->>>:配置的组合规则商品材质组合:{}", domain.getLen(), JSON.toJSONString(domain));
            if (CollectionUtils.isEmpty(materialIds)) {
                log.error(":>>> 证书材质:已经匹配完成,跳出内层循环");
                break;
            }
            Collections.sort(domain.getArray());
            Collections.sort(materialIds);
            if (domain.getArray().size() >= materialIds.size()) {
                SingleMatchDomain matchDomain = this.match(materialIds, domain.getArray());
                if (Objects.nonNull(matchDomain)) {
                    matchDomains.add(matchDomain);
                    if (!CollectionUtils.isEmpty(matchDomain.getMatch())) {
                        materialIds.removeAll(matchDomain.getMatch());
                        log.info(":>>> 组合数量大于等于实际数量,匹配上,从待处理中删除:{},剩余待匹配:{}", JSON.toJSONString(matchDomain.getMatch()), JSON.toJSON(materialIds));
                    }
                }

            } else {
                SingleMatchDomain matchDomain = this.match(domain.getArray(), materialIds);
                if (Objects.nonNull(matchDomain)) {
                    matchDomains.add(matchDomain);
                    if (!CollectionUtils.isEmpty(matchDomain.getMatch())) {
                        materialIds.removeAll(matchDomain.getMatch());
                        log.info(":>>> 组合数量小于于实际数量,匹配上,从待处理中删除:{},剩余待匹配:{}", JSON.toJSONString(matchDomain.getMatch()), JSON.toJSON(materialIds));
                    }
                }

            }
        }
        Collections.sort(matchDomains);
        log.info(":>>> 排序得到的合单处理结果:{}", JSON.toJSONString(matchDomains));
        for (SingleMatchDomain singleMatchDomain : matchDomains) {
            if (!CollectionUtils.isEmpty(singleMatchDomain.getMatch())) {
                doneResult.add(singleMatchDomain.getMatch());
                log.info(":>>> 处理结果添加:{}", JSON.toJSONString(singleMatchDomain.getMatch()));
            }
        }
        if (!CollectionUtils.isEmpty(materialIds)) {
            log.info(":>>> 还有未匹配的商品:{}", JSON.toJSONString(materialIds));
            for (String s : materialIds) {
                List<String> ss = new ArrayList<>();
                ss.add(s);
                doneResult.add(ss);
            }
        }
        log.info(":>>> 处理合单的结果为:包裹个数:{} ,{}", doneResult.size(), JSON.toJSONString(doneResult));
        // 缓存去重复
        List<String> sss = new ArrayList<>();
        List<List<String>> otherCa = new ArrayList<>();
        for (int i = 0; i < doneResult.size(); i++) {
            boolean hasCa = false;
            int caNum = 0;
            List<String> subOrderIds = new ArrayList<>();
            // 一个包裹
            log.info(":>>> 一个包裹的商品:{},商品数量:{} ,{}", i, doneResult.get(i).size(), JSON.toJSON(doneResult.get(i)));
            for (String j : doneResult.get(i)) {
                // 商品和材质对应,J 是材质
                log.info(":>>> 商品:{} 对应子订单个数:{}", j, itemMapNow.get(j).size());
                for (OOrderInfo oOrderInfo : itemMapNow.get(j)) {
                    // 可能会有问题
                    CaInfo caInfo = getCaInfo(oOrderInfo.getItemInfoId());
                    if (Objects.isNull(caInfo)) {
                        log.error(":>>> 订单:{} 商品为空", oOrderInfo.getId());
                        continue;
                    }
                    if (Boolean.FALSE.equals(caInfo.getCaGift())) {
                        log.info(":>>> 订单:{},商品:{} 为证书", oOrderInfo.getId(), caInfo.getName());
                        caNum++;
                        if (caNum > 1) {
                            log.info(":>>> 订单:{},商品:{} 为证书,包裹中已经有证书", oOrderInfo.getId(), caInfo.getName());
                            hasCa = true;
                        }
                    }
                    if (Boolean.FALSE.equals(caInfo.getCaGift()) & hasCa && !sss.contains(oOrderInfo.getId())) {
                        log.info(":>>> 订单:{},商品:{} 为证书,包裹中已经有证书,添加到其他证书列表中", oOrderInfo.getId(), caInfo.getName());
                        List<String> to = new ArrayList<>();
                        to.add(oOrderInfo.getId());
                        otherCa.add(to);
                    }
                    if (!sss.contains(oOrderInfo.getId())) {
                        subOrderIds.add(oOrderInfo.getId());
                        sss.add(oOrderInfo.getId());
                        log.info(":>>> 订单:{},商品:{} 添加到合单子订单中,子订单列表:{},去重复列表:{}", oOrderInfo.getId(), caInfo.getName(), JSON.toJSONString(subOrderIds), JSON.toJSONString(sss));


                    }
                }
            }
            String singleNo = this.generateCreNo();
            Query query1 = new Query().addCriteria(Criteria.where("id").in(subOrderIds));
            Update update = new Update();
            update.set("singleNo", singleNo);
            UpdateResult updateResult = mongoTemplate.updateMulti(query1, update, OOrderInfo.class);
            log.info(":>>> 更新子订单:{},合单NO:{},结果为:{}", JSON.toJSONString(subOrderIds), singleNo, updateResult.getModifiedCount());
        }
        log.info(":>>> 其他证书的信息:{}", JSON.toJSONString(otherCa));
        for (List<String> ss : otherCa) {
            String singleNo = this.generateCreNo();
            Query query1 = new Query().addCriteria(Criteria.where("id").in(ss));
            Update update = new Update();
            update.set("singleNo", singleNo);
            UpdateResult updateResult = mongoTemplate.updateMulti(query1, update, OOrderInfo.class);
            log.info(":>>> 更新子订单:{},合单NO:{},结果为:{}", JSON.toJSONString(ss), singleNo, updateResult.getModifiedCount());
        }
    }

    private static List<List<String>> getCombine(List<String> todo) {
        log.info(":>>> 待组合:{},数组大小:{}", JSON.toJSONString(todo), todo.size());
        List<List<String>> result = new ArrayList<>();
        HashMap<Integer, String> to = new HashMap<>();
        String s = "";
        for (Integer i = 0; i < todo.size(); i++) {
            to.put(i, todo.get(i));
            if (StringUtils.isBlank(s)) {
                s = i + "";
            } else {
                s = s + "" + i;
            }
        }
        ArrayList<String> res = combine(s);
        for (String aa : res) {
            List<String> aaa = new ArrayList<>();
            for (int i = 0; i < aa.length(); i++) {
                String j = aa.substring(i, i + 1);
                if (StringUtils.isNumeric(j)) {
                    aaa.add(to.get(Integer.valueOf(j)));
                }

            }
            result.add(aaa);
        }
        log.info(":>>> 字符串组合:{} 结果:{}", JSON.toJSONString(todo), JSON.toJSON(result));
        return result;
    }

    private static ArrayList<String> combine(String str) {
        ArrayList<String> result = new ArrayList<String>();
        if (str == null && str.isEmpty()) {
            return result;
        }
        // 组合  C_n^1+C_n^2 + …… + C_n^n
        for (int i = 1; i <= str.length(); i++) {
            combine(str.toCharArray(), new HashSet(i), i, result);
        }
        Collections.sort(result);
        return result;
    }

    /**
     * 计算 C_n^m 的组合次数
     *
     * @param pres   选中的元素
     * @param m      选中的个数
     * @param result 返回的结果
     */
    private static void combine(char[] chars, HashSet pres, int m, ArrayList<String> result) {
        if (pres.size() == m) {
            // 将容器转换为字符串
            String stack = String.valueOf(pres);
            // 去除重复元素
            if (!result.contains(stack)) {
//                System.out.println(stack);
                result.add(stack);
            }
            return;
        }
        for (int i = 0, len = chars.length; i < len; i++) {
            swap(chars, 0, i);
            char[] newChars = new char[len - 1];
            // 复制未选中的剩余元素
            System.arraycopy(chars, 1, newChars, 0, len - 1);
            pres.add(chars[0]);
            combine(newChars, pres, m, result);
            // 回退一格选中的元素
            pres.remove(chars[0]);
            swap(chars, 0, i);
        }
    }

    private static void swap(char[] chars, int i, int j) {
        char tmp = chars[j];
        chars[j] = chars[i];
        chars[i] = tmp;
    }

    private String generateCreNo() {
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(orderNoPreKey, redisTemplate.getConnectionFactory());
        Long increment = entityIdCounter.getAndIncrement();
        log.info(":>>> 自增的长度为:{}", increment);
        if ((null == increment || increment.longValue() == 0)) {
            //初始设置过期时间
            entityIdCounter.expire(orderNoExpireTime, TimeUnit.SECONDS);
        }
        // goodsCode对应证书编码,yyyyMMddHHmmssSSS
        String orderNo = "S" +
                TimeUtils.formatDate(new Date(), "yyyyMMdd") + entityIdCounter;
        return orderNo;
    }

    private CaInfo getCaInfo(String id) {
        if (caInfoCache.contains(id)) {
            return caInfoCache.get(id);
        }
        CaInfo caInfo = caInfoService.findOne(id);
        if (!Objects.isNull(caInfo)) {
            caInfoCache.put(id, caInfo);
            return caInfo;
        }
        return null;
    }

    private CaMaterialConfig getCaMaterialConfig(String id) {
        if (caMaterialCache.contains(id)) {
            return caMaterialCache.get(id);
        }
        CaMaterialConfig c = caMaterialConfigService.findOne(id);
        if (!Objects.isNull(c)) {
            caMaterialCache.put(id, c);
            return c;
        }
        return null;
    }

    private SingleMatchDomain match(List<String> m1, List<String> m2) {
        if (CollectionUtils.isEmpty(m1) || CollectionUtils.isEmpty(m2)) {
            return null;
        }
        SingleMatchDomain s = new SingleMatchDomain();
        if (CollectionUtils.isEmpty(m1) || CollectionUtils.isEmpty(m2)) {
            s.setRatio(0);
        }
        for (String s1 : m1) {
            for (String s2 : m2) {
                if (s1.equals(s2)) {
                    s.getMatch().add(s1);
                    s.setRatio(s.getRatio() == null ? 0 : s.getRatio() + 1);
                }
            }
        }
        return s;
    }
}
